﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;

namespace Paint.NET_CRAW
{
    public enum MtrxStorageType: byte
    {
        tint24 = 3,
        tfloat = 4,
        tdouble = 8
    }

    /// <summary>
    /// Raw Image format driver
    /// </summary>
    public class RawDriver
    {
        public uint width, height;
        string fmttag;
        private Stream Input;
        public MtrxStorageType datatype;

        /// <summary>
        /// Creates a new Raw Image format driver
        /// </summary>
        /// <param name="inp">A stream to write in or read from</param>
        public RawDriver(Stream inp)
        {
            this.width = 0;
            this.height = 0;
            this.datatype = MtrxStorageType.tint24;
            this.fmttag = "CSRAW";
            this.Input = inp;
        }

        /// <summary>
        /// Closes the stream
        /// </summary>
        public void Close()
        {
            this.Input.Close();
        }

        private byte[] HeaderToByteArray()
        {
            List<byte> data = new List<byte>();
            foreach (char c in this.fmttag) data.Add((byte)c);
            data.AddRange(BitConverter.GetBytes(this.width));
            data.AddRange(BitConverter.GetBytes(this.height));
            data.Add((byte)this.datatype);
            return data.ToArray();
        }

        private void HeaderFromByteArray(byte[] array)
        {
            if (array.Length < 14) throw new ArgumentException("array has invalid length");
            string fmtag = null;
            for (int i = 0; i < 5; i++)
            {
                fmtag += (char)array[i];
            }
            if (fmtag != this.fmttag) throw new NotSupportedException("Unsuported file type");
            this.width = BitConverter.ToUInt32(array, 5);
            this.height = BitConverter.ToUInt32(array, 9);
            this.datatype = (MtrxStorageType)array[13];
        }

        public void WriteBitmap(Bitmap img, MtrxStorageType t)
        {
            this.width = (uint)img.Width;
            this.height = (uint)img.Height;
            this.datatype = t;
            BinaryWriter bw = new BinaryWriter(this.Input);
            Int24 cnv = new Int24();
            Color c;
            bw.Write(this.HeaderToByteArray());
            for (int i = 0; i < img.Height; i++)
            {
                for (int j = 0; j < img.Width; j++)
                {
                    c = img.GetPixel(j, i);
                    cnv.FromColor(c);
                    switch (this.datatype)
                    {
                        case MtrxStorageType.tdouble:
                            bw.Write((double)cnv.ToInt32());
                            break;
                        case MtrxStorageType.tfloat:
                            bw.Write((float)cnv.ToInt32());
                            break;
                        case MtrxStorageType.tint24:
                            bw.Write(cnv.ToByteArray());
                            break;
                    }
                }
            }
            //bw = null;
            //this.Input.Close();
        }

        public Bitmap ReadBitmap()
        {
            BinaryReader br = new BinaryReader(this.Input);
            this.HeaderFromByteArray(br.ReadBytes(14));
            Int24 cnv = new Int24();
            Bitmap ret = new Bitmap((int)this.width, (int)this.height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            byte[] temp;
            for (int i = 0; i < this.height; i++)
            {
                for (int j = 0; j < this.width; j++)
                {
                    switch (this.datatype)
                    {
                        case MtrxStorageType.tdouble:
                            cnv.FromInt32(Convert.ToInt32(br.ReadDouble()));
                            ret.SetPixel(j, i, cnv.ToColor());
                            break;
                        case MtrxStorageType.tfloat:
                            cnv.FromInt32(Convert.ToInt32(br.ReadSingle()));
                            ret.SetPixel(j, i, cnv.ToColor());
                            break;
                        case MtrxStorageType.tint24:
                            temp = br.ReadBytes(3);
                            Array.Reverse(temp);
                            cnv.FromByteArray(temp);
                            ret.SetPixel(j, i, cnv.ToColor());
                            break;
                    }
                }
            }
            //br = null;
            //this.Input.Close();
            return ret;
        }
    }
}
